<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>计算属性和侦听器</title>
    <!-- 开发环境版本，包含了有帮助的命令行警告 -->
    <script src="./js/vue.js"></script>
  </head>
  <!-- 
    模板内的表达式非常便利，但是设计它们的初衷是用于简单运算的。
    在模板中放入太多的逻辑会让模板过重且难以维护。
 -->
  <body>
    <div id="app">
      <!-- 
          在这个地方，模板不再是简单的声明式逻辑。你必须看一段时间才能意识到，这里是想要显示变量 message 的翻转字符串。
          当你想要在模板中的多处包含此翻转字符串时，就会更加难以处理。
        所以，对于任何复杂逻辑，你都应当使用计算属性。
       -->
    </div>
    <div id="example">
      <!-- 
          基础例子
       -->
      {{ message.split('').reverse().join('') }}
      <p>Original message: "{{ message }}"</p>
      <p>Computed reversed message: "{{ reversedMessage }}"</p>
      <!-- 
        计算属性缓存 vs 方法
     -->
      <!-- 
         我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。
         然而，不同的是计算属性是基于它们的响应式依赖进行缓存的。
         只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变
         ，多次访问 reversedMessage 计算属性会立即返回之前的计算结果，而不必再次执行函数。
      -->
      <p>Reversed message: "{{ reversedMessage1() }}"</p>
      <!-- 
          这也同样意味着下面的计算属性将不再更新，
          因为 Date.now() 不是响应式依赖：
       -->
      <p>{{now}}</p>
      <!-- 
          相比之下，每当触发重新渲染时，调用方法将总会再次执行函数。
        我们为什么需要缓存？假设我们有一个性能开销比较大的计算属性 A，
        它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的
        计算属性依赖于 A。如果没有缓存，我们将不可避免的多次执行 A 
        的 getter！如果你不希望有缓存，请用方法来替代。
       -->
    </div>
    <!-- 
        计算属性 vs 侦听属性
     -->
    <div id="demo">
      {{ fullName }}
      <!-- 计算属性的 setter -->
    </div>
  </body>
  <script>
    var vue = new Vue({
      el: "#app",
      data: {
        message: "zhuyijun",
      },
    });
    var vm = new Vue({
      el: "#example",
      data: {
        message: "Hello",
      },
      //   计算属性
      computed: {
        // 计算属性的 getter
        reversedMessage: function () {
          // `this` 指向 vm 实例
          return this.message.split("").reverse().join("");
        },
        now: function () {
          return Date.now();
        },
      },
      // 在组件中
      methods: {
        reversedMessage1: function () {
          return this.message.split("").reverse().join("");
        },
      },
    });
    // 你可以打开浏览器的控制台，自行修改例子中的 vm。
    // vm.reversedMessage 的值始终取决于 vm.message 的值。
    // 你可以像绑定普通 property 一样在模板中绑定计算属性。
    // Vue 知道 vm.reversedMessage 依赖于 vm.message，
    // 因此当 vm.message 发生改变时，所有依赖 vm.reversedMessage
    // 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系：
    // 计算属性的 getter 函数是没有副作用 (side effect) 的，
    // 这使它更易于测试和理解。
    console.log(vm.reversedMessage); // => 'olleH'
    vm.message = "Goodbye";
    console.log(vm.reversedMessage); // => 'eybdooG'
    // 监听属性
    var vm1 = new Vue({
      el: "#demo",
      data: {
        firstName: "Foo",
        lastName: "Bar",
        // fullName: "Foo Bar",
      },
      //   watch: {
      //     firstName: function (val) {
      //       this.fullName = val + " " + this.lastName;
      //     },
      //     lastName: function (val) {
      //       this.fullName = this.firstName + " " + val;
      //     },
      //   },
      computed: {
        // fullName: function () {
        //   return this.firstName + " " + this.lastName;
        // },
        fullName: {
          // getter
          get: function () {
            return this.firstName + " " + this.lastName;
          },
          // setter
          set: function (newValue) {
            var names = newValue.split(" ");
            this.firstName = names[0];
            this.lastName = names[names.length - 1];
          },
        },
      },
    });
    vm1.fullName = "John Doe";
  </script>
</html>
